package com.xyy.saas.payment.query.core.trade.impl;

import com.alibaba.fastjson.JSON;
import com.xyy.saas.payment.adpater.RouteManager;
import com.xyy.saas.payment.adpater.pingxx.domain.PayDO;
import com.xyy.saas.payment.callback.core.result.CallbackRunner;
import com.xyy.saas.payment.common.RocketMqTag;
import com.xyy.saas.payment.common.constants.ChannelConstants;
import com.xyy.saas.payment.common.exception.PaymentException;
import com.xyy.saas.payment.cores.bo.TransactionResultBO;
import com.xyy.saas.payment.cores.enums.ResultCodeEnum;
import com.xyy.saas.payment.cores.enums.TransactionStatusEnum;
import com.xyy.saas.payment.cores.enums.TransactionTypeEnum;
import com.xyy.saas.payment.cores.param.TransactionParam;
import com.xyy.saas.payment.cores.vo.ResultVO;
import com.xyy.saas.payment.dao.model.PayResultOrder;
import com.xyy.saas.payment.dao.model.PrePayOrder;
import com.xyy.saas.payment.dao.model.RefundOrder;
import com.xyy.saas.payment.payment.core.service.PrePayOrderService;
import com.xyy.saas.payment.payment.core.service.RefundOrderService;
import com.xyy.saas.payment.query.core.trade.AbstractTransactionQuery;
import com.xyy.saas.payment.service.PayResultOrderService;
import com.xyy.saas.payment.util.DateUtil;
import com.xyy.saas.payment.util.JSONUtils;
import com.xyy.saas.payment.util.TimestampConverter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.Date;
import java.util.List;

@Component
@Slf4j
public class TransactionPayQuery extends AbstractTransactionQuery {

    @Autowired
    private PrePayOrderService prePayOrderService;
    @Autowired
    private PayResultOrderService payResultOrderService;
    @Autowired
    private RefundOrderService refundOrderService;
    @Autowired
    private RouteManager routeManager;
    @Autowired
    private CallbackRunner callbackRunner;

    @Override
    protected ResultVO checkParam(TransactionParam param) {
        if(null == param) {
            return ResultVO.create(ResultCodeEnum.PARAM_ERROR);
        }
        if(StringUtils.isBlank(param.getTradeNo())) {
            return ResultVO.create(ResultCodeEnum.TRADE_NOT_EXIT_ERROR);
        }
        if(StringUtils.isBlank(param.getChannel())) {
            return ResultVO.createError("渠道不能为空");
        }
        if(StringUtils.isBlank(param.getPayMode())) {
            return ResultVO.createError("支付方式不能为空");
        }
        return ResultVO.createSuccess();
    }

    @Override
    protected TransactionResultBO queryLocalRecord(TransactionParam param) throws PaymentException {
        // 先查数据库
        PrePayOrder prePayOrder = prePayOrderService.getPrePayOrderByPayNo(param.getTradeNo());
        if (prePayOrder == null) {
            throw new PaymentException(ResultCodeEnum.TRADE_NOT_EXIT_ERROR);
        }
        PayResultOrder payResultOrder = payResultOrderService.selectByPayNoAndChannel(param.getTradeNo(), param.getChannel());
        if(null != payResultOrder) {
            return TransactionResultBO.build(param.getTradeNo(), TransactionStatusEnum.SUCCESS.getCode(), payResultOrder.getAmount(), payResultOrder.getPayChannel(), DateUtil.dateToLong(payResultOrder.getCtime()), payResultOrder.getChannelChargeNo(), prePayOrder.getBusinessOrderNo(), payResultOrder.getMerchantId());
        }
        return TransactionResultBO.build(param.getTradeNo(), TransactionStatusEnum.TRANSACTING.getCode());
    }

    @Override
    protected boolean checkIfQueryChannel(TransactionResultBO resultBO) {
        // 成功就不查，否则查渠道
        if(resultBO != null && TransactionStatusEnum.SUCCESS.getCode().equals(resultBO.getStatus())) {
            return false;
        }
        return true;
    }

    @Override
    protected TransactionResultBO queryChannelRecord(TransactionParam param) throws PaymentException {
        PrePayOrder prePayOrder = prePayOrderService.getPrePayOrderByPayNo(param.getTradeNo());

        if(null == prePayOrder) {
            throw new PaymentException(ResultCodeEnum.TRADE_NOT_EXIT_ERROR);
        }
        param.setBusinessType(prePayOrder.getBusinessType());
        param.setBusinessOrderType(prePayOrder.getBusinessOrderType());
        param.setAccountId(prePayOrder.getReceiverId());
        param.setTradeTime(prePayOrder.getCreateTime());
        param.setPayer(prePayOrder.getPayer());
        Object channelObj = JSONUtils.getValueFromJson(prePayOrder.getExtra(), "channel");
        param.setPayChannel(channelObj == null ? "": (String) channelObj);
        PayResultOrder payResultOrder = payResultOrderService.selectByPayNoAndChannel(param.getTradeNo(), param.getChannel());

        if(null != payResultOrder) {
            // 强制走渠道查询有效
            return routeManager.getAdapter(payResultOrder.getPayChannel(), StringUtils.EMPTY, payResultOrder.getReceiveId()).queryTransaction(param);
        }

        return routeManager.queryTransaction(param);
    }

    @Override
    protected void callback(TransactionResultBO channelRecord) {
        log.info("TransactionPayQuery#callback channelRecord:{}", JSON.toJSONString(channelRecord));
        if (channelRecord == null || StringUtils.isBlank(channelRecord.getTradeNo()) || StringUtils.isBlank(channelRecord.getChannel())) {
            return;
        }
        PrePayOrder prePayOrder = prePayOrderService.getPrePayOrderByPayNo(channelRecord.getTradeNo());
        if (prePayOrder == null) {
            log.error("TransactionPayQuery#callback order not exit error payNo:{}", channelRecord.getTradeNo());
            return;
        }
        PayResultOrder payResultOrder = payResultOrderService.selectByPayNoAndChannel(channelRecord.getTradeNo(), channelRecord.getChannel());
        if (payResultOrder == null) {
            log.error("TransactionPayQuery#callback payResultOrder is null, payNo:{}", channelRecord.getTradeNo());
            return;
        }

        PayDO payDO = new PayDO();
        fillPayDO(payDO, prePayOrder, payResultOrder);
        callbackRunner.callbackPingXXWithTag(RocketMqTag.TOPIC_PINGXX_CALLBACK_QUERY_TRADE_TAG, payDO);
    }

    private void fillPayDO(PayDO payDO, PrePayOrder prePayOrder, PayResultOrder payResultOrder) {
        payDO.setNotifyTime(DateUtil.now());
        payDO.setChannel(payResultOrder.getPayChannel());
        payDO.setOrderNo(prePayOrder.getPayNo());
        payDO.setAmount(payResultOrder.getAmount());
        payDO.setBusinessType(prePayOrder.getBusinessType());
        payDO.setBusinessOrderType(prePayOrder.getBusinessOrderType());
        payDO.setReceiverId(prePayOrder.getReceiverId());
        payDO.setCallbackAddress(prePayOrder.getCallbackAddress());
        payDO.setPayer(prePayOrder.getPayer());
        payDO.setCount(0);
        payDO.setBusinessOrderNo(prePayOrder.getBusinessOrderNo());
        payDO.setChannelChargeNo(payResultOrder.getChannelChargeNo());
        payDO.setPayMode(payResultOrder.getPayMode());
        payDO.setChannelTransactionNo(payResultOrder.getChannelTransactionNo());
        payDO.setMchId(payResultOrder.getMerchantId());
        payDO.setTimePaid(payResultOrder.getCtime());
    }

    @Override
    protected void processChannelRecord(TransactionParam param, TransactionResultBO channelRecord) throws PaymentException{
        String tradeNo = channelRecord.getTradeNo();
        PrePayOrder prePayOrder = prePayOrderService.getPrePayOrderByPayNo(channelRecord.getTradeNo());
        channelRecord.setBusinessOrderNo(prePayOrder.getBusinessOrderNo());
        List<RefundOrder> refundOrders = refundOrderService.selectListByPayNo(tradeNo);
        if(TransactionStatusEnum.REFUNDED.getCode().equals(channelRecord.getStatus())
            || TransactionStatusEnum.TRADE_CLOSE.getCode().equals(channelRecord.getStatus())) {
            if(!CollectionUtils.isEmpty(refundOrders)){
                if(channelRecord.getChannel().contains(ChannelConstants.CHANNEL_WX_NAME)) {
                    // 微信渠道 REFUND 转入退款
                    channelRecord.setStatus(TransactionStatusEnum.SUCCESS.getCode());
                }
                if(channelRecord.getChannel().contains(ChannelConstants.CHANNEL_ALI_NAME)) {
                    // 支付宝渠道 TRADE_CLOSED（未付款交易超时关闭，或支付完成后全额退款）
                    channelRecord.setStatus(TransactionStatusEnum.SUCCESS.getCode());
                }
            }else{
                // 判为失败单子
                channelRecord = TransactionResultBO.build(tradeNo, TransactionStatusEnum.FAIL.getCode());
            }
        }
        PayResultOrder payResultOrder = payResultOrderService.selectByPayNoAndChannel(tradeNo, param.getChannel());
        if(null == payResultOrder && TransactionStatusEnum.SUCCESS.getCode().equals(channelRecord.getStatus())) {
            PayResultOrder payResultOrder1 = new PayResultOrder();
            Date successDate = TimestampConverter.safeConvert(channelRecord.getSuccessTime());
            payResultOrder1.setCtime(successDate != null ? successDate : new Date(channelRecord.getSuccessTime()));
            payResultOrder1.setUtime(new Date());
            payResultOrder1.setReceiveId(prePayOrder.getReceiverId());
            payResultOrder1.setAmount(prePayOrder.getAmount());
            payResultOrder1.setFee(0);
            payResultOrder1.setBusinessOrderType(prePayOrder.getBusinessOrderType());
            payResultOrder1.setBusinessType(prePayOrder.getBusinessType());
            payResultOrder1.setPayChannel(channelRecord.getChannel());
            payResultOrder1.setChannelChargeNo(channelRecord.getBankChargeNo());
            payResultOrder1.setPayNo(prePayOrder.getPayNo());
            payResultOrder1.setPayer(prePayOrder.getPayer());
            payResultOrder1.setChannelTransactionNo(channelRecord.getChannelChargeNo());
            payResultOrder1.setMerchantId(channelRecord.getMerchantId());
            payResultOrder1.setPayMode(param.getPayMode());
            // 付款了，又退款了
            payResultOrder1.setRefund(CollectionUtils.isEmpty(refundOrders) ? (byte) 0 : (byte) 1);
            int count = payResultOrderService.insertSelective(payResultOrder1);
            if (count < 1) {
                log.error("QueryTradeService checkPay payResultOrderService.insertSelective error payResultOrder:{}", JSON.toJSON(payResultOrder1));
                throw new PaymentException(ResultCodeEnum.DATABASE_ERROR);
            }

            // 主动查询渠道交易状态成功（查询渠道先于渠道回调）
            param.setCallback(true);
        }
    }

    @Override
    public TransactionTypeEnum getTransactionType() {
        return TransactionTypeEnum.PAY;
    }
}
